/*  
  -- The Lime II Project -- 

  A tuplespaces-based middleware for coordinating agents and mobile hosts.
  Copyright (C) 2005.
  Gian Pietro Picco, Amy L. Murphy, Lorenzo Bellini.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  Lesser General Public License for more details.

  You should have received this copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

package lime2.ca.treeplain;

import java.io.*;
import java.net.*;

import lime2.CommunicationMgr;
import lime2.LimeCommunicationException;
import lime2.LimeConstants;
import lime2.LimeServer;
import lime2.LimeServerID;
import lime2.Location;
import lime2.RemoteOp;
import lime2.ca.CastInfo;
import lime2.ca.EngMsg;
import lime2.ca.ICommunicationAdapter;


/**
 * A "receiver" of remote unicast messages. It accepts as many new (TCP)
 * connections as possible, receives remote ops and enqueues them.
 * 
 * @author Gian Pietro Picco
 * @author Amy Murphy
 * @author Lorenzo Bellini
 */

class TreePlainUnicastReceiver extends Thread {
	/**
	 * Unicast server socket
	 */
	private ServerSocket ssock;
	
	private boolean shutdown;
	

	/**
	 * Constructor method.
	 * 
	 * @param SSock
	 *            server socket
	 */
	TreePlainUnicastReceiver(ServerSocket SSock) {
		super();
		this.ssock = SSock;
		this.shutdown = false;
	}

	/**
	 * Run method.
	 */
	public void run() {
		
		while (!shutdown) {
			try {
				Socket newSocket = ssock.accept();
				Connection.add(newSocket);
				UnicastServant unicastservant = new UnicastServant(newSocket);
				unicastservant.start();
			} catch (Exception ioe) {
				ioe.printStackTrace();
			}
		}

		// closes server socket
		try {
			ssock.close();
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}

	/**
	 * Shuts the receiver down.
	 */
	void shutdown() {
		shutdown = true;
	}
}



/**
 * An instance of UnicastServant manages an incoming connection. The server-side
 * policy is to close the connection as "second": it is the client-side that
 * chooses whether to keep the connection opened or not.
 */

class UnicastServant extends Thread {
	// private data members
	private Socket socket;
	
	private ICommunicationAdapter ca;

	/**
	 * Constructor method.
	 * 
	 * @param socket
	 *            to listen to
	 */
	UnicastServant(Socket socket) {
		super();
		this.socket = socket;
		ca = CommunicationMgr.get().getCA();
	}

	UnicastServant(Socket socket, LimeServerID node) {
		super();
		this.socket = socket;
		ca = CommunicationMgr.get().getCA();
	}

	/**
	 * Run method.
	 */
	public void run() {
		// initializations
		ObjectInputStream ois = null;
		boolean close = false;

		// opens object stream
		try {
			ois = new ObjectInputStream(socket.getInputStream());
		} catch (IOException ioe) {
			close = true;
		}

		// main routine
		while (!close) {
			try {
				Object o = ois.readObject();
				if (o instanceof RemoteOp) {
					RemoteOp ro = (RemoteOp) o;
//					System.out.println("recevier unicast remote op:" +ro.toString()+" from " +ro.getSourceString());
					CastInfo cast = ro.getCastInfo();
					if (cast != null) {
						//perform multicast, and route the packets 
						try {
							cast.performMulticast(ca, ro);
						} catch (LimeCommunicationException e) {
							e.printStackTrace();
						}
					}
					
					TreePlainCA.queue.add(o);
					
				} else if (o instanceof EngMsg) {
					EngMsg msg = (EngMsg)o;
					
					if (msg.type == LimeConstants.HELLO_REPLY) {
						long delay = System.nanoTime() - msg.sendHelloTime;
						Mesh.getInstance().updateMeshItem(Location.HERE.getID(), msg.source, delay);
					}
				}
			} catch (ClassNotFoundException cnfe) {
				cnfe.printStackTrace();
			} catch (IOException ioe) {
				close = true;
			}
		}

		// must close connection
		try {
			if (ois != null)
				ois.close();
		} catch (IOException ioe) {
		}
		try {
			Connection.remove(socket);
		} catch (IOException ioe) {
		}
		try {
			socket.close();
		} catch (IOException ioe) {
		}
	}

}
